package com.gitee.base.security.permission;

import com.gitee.base.modules.core.model.SysPermission;
import com.gitee.base.modules.core.service.ISysPermissionService;
import com.gitee.base.modules.core.service.ISysRoleService;
import com.gitee.base.modules.core.service.ISysUserService;
import com.gitee.base.security.permission.annotation.RequiresPermissionsDesc;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.reflect.MethodUtils;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.core.BridgeMethodResolver;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;
import org.springframework.util.ClassUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 同样的菜单下，按钮不可重名
 */
@Component
public class PermissionResolver {
    @Autowired
    private ApplicationContext applicationContext;

    @Autowired
    private ISysUserService sysUserService;
    @Autowired
    private ISysRoleService sysRoleService;
    @Autowired
    private ISysPermissionService sysPermissionService;


    public List<String> permissionsByRoleIds(String roleIds){
        if(StringUtils.isBlank(roleIds)) return Collections.EMPTY_LIST;
        List<Permission> hasPermissionList = permissionsByRoleIds(Arrays.stream(roleIds.split(",")).mapToInt(Integer::valueOf).toArray());
        return hasPermissionList.stream().flatMap(p-> p.getChildren().stream()).flatMap(p->p.getChildren().stream()).map(Permission::getPermission).collect(Collectors.toList());
    }

    public List<Permission> permissionsByRoleIds(int... roleIds){
        List<SysPermission> sysPermissions = this.sysPermissionService.list(new LambdaQueryWrapper<SysPermission>().in(SysPermission::getRoleId,Arrays.stream(roleIds).boxed().collect(Collectors.toList())));
        if(sysPermissions!=null && !sysPermissions.isEmpty()){
            Optional<SysPermission> optionalSysPermission = sysPermissions.stream().filter(p -> p.getPermission().contains("*")).findAny();
            if(optionalSysPermission.isPresent()){
                return permissions();
            }else{
                List<String> rolePermissions = sysPermissions.stream().map(SysPermission::getPermission).distinct().collect(Collectors.toList());
                List<Permission> allPermissions = permissions();
                for(int f=0;f<allPermissions.size();f++){
                    Permission folder = allPermissions.get(f);
                    for(int m=0;m<folder.getChildren().size();m++){
                        Permission menu = folder.getChildren().get(m);
                        for(int b=0;b<menu.getChildren().size();b++){
                            Permission button = menu.getChildren().get(b);
                            if(!rolePermissions.contains(button.getPermission())){
                                menu.getChildren().remove(b);
                                b--;
                            }
                        }
                        if(menu.getChildren().isEmpty()){
                            folder.getChildren().remove(m);
                            m--;
                        }
                    }
                    if(folder.getChildren().isEmpty()){
                        allPermissions.remove(f);
                        f--;
                    }
                }
                return allPermissions;
            }
        }
        return Collections.EMPTY_LIST;
    }

    public List<Permission> permissions() {
        String basePackage = "com.gitee.base.modules";
        Map<String, Object> beans = this.applicationContext.getBeansWithAnnotation(Controller.class);
        Map<String, Object> restBeans = this.applicationContext.getBeansWithAnnotation(RestController.class);
        if(beans==null) return null;
        if(restBeans!=null && !restBeans.isEmpty()) beans.putAll(restBeans);

        List<Permission> permissionList = new ArrayList<>();
        for (Map.Entry<String, Object> beanEntry : beans.entrySet()) {
            Object bean = beanEntry.getValue();
            if (!StringUtils.contains(ClassUtils.getPackageName(bean.getClass()), basePackage)) {
                continue;
            }

            Class controllerClz = bean.getClass().getSuperclass();
            RequestMapping clazzRequestMapping = AnnotationUtils.findAnnotation(controllerClz, RequestMapping.class);
            List<Method> methods = MethodUtils.getMethodsListWithAnnotation(controllerClz, RequiresPermissions.class);
            for (Method method : methods) {
                RequiresPermissions requiresPermissions = AnnotationUtils.getAnnotation(method, RequiresPermissions.class);
                RequiresPermissionsDesc requiresPermissionsDesc = AnnotationUtils.getAnnotation(method, RequiresPermissionsDesc.class);
                if (requiresPermissions == null || requiresPermissionsDesc == null) continue;
                String path = clazzRequestMapping != null ? clazzRequestMapping.value()[0] : "";
                Annotation[] annotations = BridgeMethodResolver.findBridgedMethod(method).getDeclaredAnnotations();
                if(annotations.length>0){
                    for (Annotation annotation:annotations){
                        if(annotation instanceof RequestMapping || annotation instanceof GetMapping || annotation instanceof PostMapping){
                            String requestMethod = "GET";
                            if(annotation instanceof PostMapping){
                                requestMethod = "POST";
                            }
                            if(annotation instanceof RequestMapping){
                                requestMethod = "GET|POST";
                            }

                            //------------------------
                            Permission folder = null;
                            for(Permission perm:permissionList){
                                if(perm.getName().equals(requiresPermissionsDesc.folder())){
                                    folder = perm;
                                    break;
                                }
                            }

                            if (folder==null){
                                folder = new Permission();
                                folder.setId(DigestUtils.md5Hex(requiresPermissionsDesc.folder()));
                                folder.setName(requiresPermissionsDesc.folder());
                                folder.setChildren(new ArrayList<>());
                                permissionList.add(folder);
                            }

                            Permission menu = null;
                            for (Permission perm : folder.getChildren()){
                                if (requiresPermissionsDesc.folder().equals(folder.getName()) && requiresPermissionsDesc.menu().equals(perm.getName())){
                                    menu = perm;
                                    break;
                                }
                            }
                            if (menu==null){
                                menu = new Permission();
                                menu.setId(DigestUtils.md5Hex(requiresPermissionsDesc.folder()+"-"+requiresPermissionsDesc.menu()));
                                menu.setName(requiresPermissionsDesc.menu());
                                menu.setChildren(new ArrayList<>());
                                folder.getChildren().add(menu);
                            }

                            if(requiresPermissionsDesc.page()){
                                Object annotationValue = AnnotationUtils.getValue(annotation);
                                if(annotationValue!=null){
                                    menu.setPath(path + ((String[])annotationValue)[0]);
                                }
                            }

                            Permission button = null;
                            for (Permission perm : menu.getChildren()) {
                                if (requiresPermissionsDesc.button().equals(perm.getName())) {
                                    button = perm;
                                    break;
                                }
                            }
                            if (button == null) {
                                if (StringUtils.isNotBlank(requiresPermissionsDesc.button())) {
                                    button = new Permission();
                                    button.setId(DigestUtils.md5Hex(requiresPermissionsDesc.folder()+"-"+requiresPermissionsDesc.menu()+"-"+requiresPermissionsDesc.button()));
                                    button.setName(requiresPermissionsDesc.button());
                                    button.setRequestMethod(requestMethod);
                                    button.setPermission(String.join(",",requiresPermissions.value()));
                                    Object annotationValue = AnnotationUtils.getValue(annotation);
                                    if(annotationValue!=null){
                                        button.setPath(path + ((String[])annotationValue)[0]);
                                    }
                                    if(requiresPermissionsDesc.page()){
                                        menu.getChildren().add(0,button);
                                    }else{
                                        menu.getChildren().add(button);
                                    }

                                }
                            }
                        }
                    }
                }
            }
        }
        return permissionList;
    }
}
